require "csv"

class NEMwebParser < CSV

  attr_accessor :contents
  attr_accessor :_tables

  def initialize
    @contents = []
    @_tables = []
  end

  # Returns a new instance of NEMwebParser with contents populated by a
  # csv formatted string
  #
  def self.from_string(csv_string)
    inst = self.new
    inst.contents = self.parse csv_string
    return inst
  end  

  # Returns an array of NEMwebTable instances, one instance per table in 
  # the NEMweb CSV
  #
  def tables
    @contents.each do |row|
      handle_row row
    end

    return @_tables
  end

  # Returns an array of headers from the first table in the NEMweb CSV
  # 
  def headers
    table = tables.first
    return table.headers
  end

  # Returns the payload from the first table in the NEMweb CSV
  #
  def payload
    table = tables.first
    return table.payload
  end

  # Returns the meta row from the first table in the NEMweb CSV.
  #
  def meta
    table = tables.first
    return table.meta
  end

  def handle_row(row)
    dispatch = {
      'C' => self.method(:handle_meta),
      'I' => self.method(:handle_header),
      'D' => self.method(:handle_payload),
    }
    fn = dispatch[row.first.strip]
    return fn.call row[1..-1]
  end

  def handle_meta(row)
    if row.first == "END OF REPORT"
    else
      table = NEMwebTable.new
      table.meta = row
      @_tables.push table
    end
  end

  def handle_header(row)
    @_tables[-1].headers = row
  end

  def handle_payload(row)
    @_tables[-1].payload.push row
  end

end

# A convenience class that holds the headers payload and meta information
# from a NEMweb formatted csv.
# 
class NEMwebTable
  attr_accessor :headers
  attr_accessor :payload
  attr_accessor :meta
  def initialize
    @headers = []
    @payload = []
    @meta = []
  end

  # Returns a single diminsional array of payload values from the header 
  # column that (case-insensitive) matches `column_name`.
  # 
  # Args:
  #  column_name - string or symbol name of column to return. 
  # 
  def method_missing(column_name, *args)
    column_name = column_name.to_s.downcase
    column_index = @headers.index {|col| col.downcase == column_name }
    if column_index.nil?
      raise NoMethodError.
       new("undefined method #{column_name} for #{inspect}:#{self.class}")
    end
   
    return @payload.transpose[column_index]
  end
end
